#include <QNetworkAccessManager>
#include <QCoreApplication>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDir>

#include "download.h"

DownloadThread::DownloadThread(QObject *parent) : QThread(parent)
{
    this->downloading = false;
    this->isBreak = false;
    this->netManager = nullptr;
    this->request = nullptr;
}

void DownloadThread::setInfo(QList<file_t> fileDataList, QString downloadDir)
{
    this->fileDataList = fileDataList;
    this->downloadDir = downloadDir;
}

void DownloadThread::setUserInfo(QString &phpSessionID, QString &name)
{
    this->phpSessionID = phpSessionID;
    this->name = name;
}

void DownloadThread::setNetworkInfo(QString hostWebUrl,
                                    QString currentBaseDir,
                                    int timeout_seconds)
{
    this->hostWebUrl = hostWebUrl;
    this->currentBaseDir = currentBaseDir;
    this->timeout_seconds = timeout_seconds;
}

void DownloadThread::downloadResponse(/*QNetworkReply *reply*/)
{
    qDebug() << "DownloadThread::" << __FUNCTION__;
    /* 过滤请求错误 */
    if(reply->error() != QNetworkReply::NoError) {
        emit finished(REQUEST_ERROR, reply->errorString());
        this->downloading = false;
        this->isBreak = true;
        return;
    }

    /* 判断响应数据 */
    QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    /* 如果解析响应状态码错误或者状态码!=200 */
    qDebug() << "downloadResponse Response statusCode:" << statusCode.toInt();
    if(!statusCode.isValid() || statusCode.toInt() != 200) {
        QVariant reason =  reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        if(reason.isValid())
            emit finished(REQUEST_ERROR, reason.toString());
        else
            emit finished(REQUEST_ERROR, "Unknown response error");
        this->downloading = false;
        this->isBreak =true;
        return ;
    }

    /* parse response data */
    QList<QByteArray> replyHeadersList = reply->rawHeaderList();
    for(int i = 0; i < replyHeadersList.size(); i++) {
        QByteArray headerByteArray = replyHeadersList.at(i);
        qDebug() << "DownloadThread::" << __FUNCTION__
                 << headerByteArray << ":" << reply->rawHeader(headerByteArray);
    }
    QString contentDisposition_Header = QString(reply->rawHeader("Content-Disposition"));
    int fileNameIndex = contentDisposition_Header.indexOf("filename=");
    fileNameIndex += QString("filename=").length();
    QString fileName = contentDisposition_Header.mid(fileNameIndex);
    QString downloadPath = this->downloadDir + "/" + fileName;
    qDebug() << "DownloadThread::" << __FUNCTION__ <<
                QString("Downloaded file path: %1").arg(downloadPath);

    /* save data to file */
    QFile file(downloadPath);
    bool isOpen = file.open(QIODevice::WriteOnly);
    if(!isOpen) {
        qDebug() << "DownloadThread::" << __FUNCTION__ <<
                    QString("Open '%1' error:%2").arg(downloadPath).arg(file.errorString());
    }
    int writeLen = file.write(reply->readAll());
    qDebug() << "DownloadThread::" << __FUNCTION__ <<
                QString("'%1' writed %2 bytes.").arg(downloadPath).arg(writeLen);
    file.close();
    this->downloading = false;
}

void DownloadThread::run()
{
    if(this->netManager == nullptr) {
        this->netManager = new QNetworkAccessManager();
        this->request = new QNetworkRequest();
//        if(connect(this->netManager,
//                &QNetworkAccessManager::finished,
//                this,
//                &DownloadThread::downloadResponse)) {
//            qDebug() << "NetworkAccessManager connect function return true.";
//        }
    }

#if 0
    for(int i = 0; i < this->fileDataList.size(); i++) {
        file_t fileData = this->fileDataList.at(i);
        QString url;
        /* http://filegeli.timechicken.cc/index.php?explorer/fileDownload&path=/libGbtsTask.so */
        url += this->hostWebUrl + QString("/index.php?explorer/fileDownload&path=/config.ini")
                /*.arg(this->currentBaseDir)
                .arg(fileData.name)*/;
        QNetworkRequest request = QNetworkRequest(QUrl(url));
        request.setRawHeader("Cookie", QString("PHPSESSID=%1; secros_user_language=zh_CN; secros_name=%2")
                             .arg(this->phpSessionID)
                             .arg(this->name)
                             .toUtf8());
        request.setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                                                            "AppleWebKit/537.36 (KHTML, like Gecko) Chrome"
                                                            "/104.0.0.0 Safari/537.36");
        request.setRawHeader("Host", this->hostWebUrl.mid(7).toUtf8());
        request.setRawHeader("Referer", QString("%1/index.php").arg(this->hostWebUrl).toUtf8());
        qDebug().noquote() << "DownloadThread::" << __FUNCTION__ <<
                    QString("Request URL:%1").arg(url);
        QList<QByteArray> requestHeaderList = request.rawHeaderList();
        for(int i = 0; i < requestHeaderList.size(); i++) {
           qDebug() << "DownloadThread::" << __FUNCTION__ <<
                       requestHeaderList.at(i) << request.rawHeader(requestHeaderList.at(i));
        }

        /* set downloading status */
        this->downloading = true;

        if(this->netManager->get(request) == nullptr) {
            qDebug() << "netManager get return nullptr";
        }
        /* waiting download done */
        while(this->downloading) {
            qDebug() << QString("Downloading '%1' ......").arg(fileData.name);
            QThread::sleep(1);
            if(this->isBreak) {
                qDebug() << "While Downloading Break";
                return ;
            }
        }
        if(this->isBreak) {
            qDebug() << "Downloading Break";
            return ;
        }
        qDebug() << QString("Downlaoding '%1' succeed.").arg(fileData.name);
    }
#else
//    QNetworkRequest request = QNetworkRequest(QUrl("http://filegeli.timechicken.cc/index.php?explorer/fileDownload&path=/config.ini"));
//    QNetworkRequest *request = new QNetworkRequest(QUrl("http://www.baidu.com"));
    this->request->setUrl(QUrl("http://www.baidu.com"));
//    request.setRawHeader("Cookie", QString("PHPSESSID=%1; secros_user_language=zh_CN; secros_name=%2")
//                         .arg(this->phpSessionID)
//                         .arg(this->name)
//                         .toUtf8());
    qDebug().noquote() << "DownloadThread::" << __FUNCTION__ <<
                QString("Request URL:%1").arg(request->url().toString());
    this->request->setHeader(QNetworkRequest::UserAgentHeader, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                                                        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome"
                                                        "/104.0.0.0 Safari/537.36");
//    request.setRawHeader("Host", this->hostWebUrl.mid(7).toUtf8());
//    request.setRawHeader("Referer", QString("%1/index.php").arg(this->hostWebUrl).toUtf8());
//    QList<QByteArray> requestHeaderList = request.rawHeaderList();
//    for(int i = 0; i < requestHeaderList.size(); i++) {
//       qDebug() << "DownloadThread::" << __FUNCTION__ <<
//                   requestHeaderList.at(i) << request.rawHeader(requestHeaderList.at(i));
//    }
    this->reply = this->netManager->get(*request);
    connect(this->reply,
            &QNetworkReply::finished,
            this,
            &DownloadThread::downloadResponse);

#endif
//    if(this->netManager != nullptr) {
//        disconnect(this->netManager,
//                &QNetworkAccessManager::finished,
//                this,
//                &DownloadThread::downloadResponse);
//        delete this->netManager;
//        this->netManager = nullptr;
//    }
//    emit finished(DownloadThread::NO_ERROR, DownloadThread::errorString(DownloadThread::NO_ERROR));
}

Download::Download(QList<file_t> &fileDataList,
                   QString currentBaseDir,
                   QString downloadDir,
                   QObject *parent) : QObject(parent)
{
    /* 异步通信的临时变量可能会调用者被释放从而访问的时候导致野引用问题。 */
//    this->fileDataList = fileDataList;

    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    for(int i = 0; i < fileDataList.size(); i++) {
        this->fileDataList.append(fileDataList.at(i));
    }
    this->init();
}

Download::Download(file_t &fileData,
                   QString currentBaseDir,
                   QString downloadDir,
                   QObject *parent) : QObject(parent)
{
    this->downloadDir = downloadDir;
    this->currentBaseDir = currentBaseDir;
    this->fileDataList.append(fileData);
    this->init();
}

Download::Status Download::init()
{
    this->settings = new ConfigFile(QCoreApplication::applicationDirPath() + "/" + DEFAULT_FILENAME);

    /* init download thread */
    this->downloadThread = new DownloadThread();

    /* connect signal and slot */
    connect(this->downloadThread,
            &DownloadThread::finished,
            this,
            &Download::downloadThreadFinished);

    return NO_ERROR;
}

Download::~Download()
{
    this->fileDataList.clear();
    delete this->settings;
    delete this->downloadThread;
}

Download::Status Download::startDownload()
{
    /* The incoming data is not checked for validity in the download thread. */

    QString name;
    QString phpSessionID;
    name = this->settings->value(USER_NAME).toString();
    phpSessionID = this->settings->value(USER_PHPSESSID).toString();
    if(name.length() == 0)          { return INVALID_NAME; }
    if(phpSessionID.length() == 0)  { return INVALID_PHPSESSID; }
    this->downloadThread->setUserInfo(phpSessionID, name);

    if(this->fileDataList.length() == 0 || this->fileDataList.size() == 0) {
        return INVALID_FILE_DATA_LIST;
    }

    if(!QDir(this->downloadDir).exists()) {
        return INVALID_DIR;
    }

    for(int i = 0; i < this->fileDataList.size(); i++) {
        file_t fileData = this->fileDataList.at(i);
        if(!fileData.isFile) {
            qDebug() << "Download::" << __FUNCTION__ << QString("'%1' is folder").arg(fileData.name);
            return FILE_IS_FOLDER;
        }
    }

    /* set download thread info */
    this->downloadThread->setInfo(this->fileDataList, this->downloadDir);
    this->downloadThread->setUserInfo(phpSessionID, name);
    this->downloadThread->setNetworkInfo(this->settings->value(USER_WEB_URL).toString(),
                                         this->currentBaseDir,
                                         10);

    this->downloadThread->start();
    return NO_ERROR;
}

void Download::downloadThreadFinished(int code, QString errorString)
{
    emit finished(code, errorString);
}

